Infer multi-file binaries like `src/bin/server/main.rs` by convention
authorMartin Sehnoutka <msehnout@redhat.com>
Fri, 23 Jun 2017 15:37:26 +0000 (17:37 +0200)
committerMartin Sehnoutka <msehnout@redhat.com>
Fri, 23 Jun 2017 15:37:26 +0000 (17:37 +0200)
src/cargo/util/toml.rs
tests/build.rs

index 8450b4d4d9b4c0296bbecf5c6714fcebcdf7285f..9cf1f5386c6f9e63aacda4c64655c2a4d482e72b 100644 (file)
@@ -52,6 +52,7 @@ impl Layout {
 
         try_add_file(&mut bins, root_path.join("src").join("main.rs"));
         try_add_files(&mut bins, root_path.join("src").join("bin"));
+        try_add_mains_from_dirs(&mut bins, root_path.join("src").join("bin"));
 
         try_add_files(&mut examples, root_path.join("examples"));
 
@@ -74,6 +75,26 @@ fn try_add_file(files: &mut Vec<PathBuf>, file: PathBuf) {
         files.push(file);
     }
 }
+
+// Add directories form src/bin which contain main.rs file
+fn try_add_mains_from_dirs(files: &mut Vec<PathBuf>, root: PathBuf) {
+    if let Ok(new) = fs::read_dir(&root) {
+        let new: Vec<PathBuf> = new.filter_map(|i| i.ok())
+            // Filter only directories
+            .filter(|i| {
+                i.file_type().map(|f| f.is_dir()).unwrap_or(false)
+                // Convert DirEntry into PathBuf and append "main.rs"
+            }).map(|i| {
+            i.path().join("main.rs")
+            // Filter only directories where main.rs is present
+        }).filter(|f| {
+            f.as_path()
+                .exists()
+        }).collect();
+        files.extend(new);
+    }
+}
+
 fn try_add_files(files: &mut Vec<PathBuf>, root: PathBuf) {
     if let Ok(new) = fs::read_dir(&root) {
         files.extend(new.filter_map(|dir| {
@@ -505,7 +526,23 @@ fn inferred_bin_targets(name: &str, layout: &Layout) -> Vec<TomlTarget> {
                       *bin == layout.root.join("src").join("main.rs") {
             Some(name.to_string())
         } else {
-            bin.file_stem().and_then(|s| s.to_str()).map(|f| f.to_string())
+            // bin is either a source file or a directory with main.rs inside.
+            if bin.ends_with("main.rs") {
+                if let Some(parent) = bin.parent() {
+                    // if the path ends with main.rs it is probably a directory, but it can also be
+                    // a file directly inside src/bin
+                    if parent.ends_with("src/bin") {
+                        bin.file_stem().and_then(|s| s.to_str()).map(|f| f.to_string())
+                    } else {
+                        parent.file_stem().and_then(|s| s.to_str()).map(|f| f.to_string())
+                    }
+                } else {
+                    None
+                }
+            } else {
+                // regular case, just a file in the bin directory
+                bin.file_stem().and_then(|s| s.to_str()).map(|f| f.to_string())
+            }
         };
 
         name.map(|name| {
index f2081c61fa3edb68d8d85f7eaf0800d7c129abf4..096927a6b6ecb6758ae7d9614c341003475c65dc 100644 (file)
@@ -3271,3 +3271,29 @@ fn no_bin_in_src_with_lib() {
                 execs().with_status(101)
                        .with_stderr_contains(r#"[ERROR] couldn't read "[..]main.rs"[..]"#));
 }
+
+
+#[test]
+fn dirs_in_bin_dir_with_main_rs() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.1.0"
+            authors = []
+        "#)
+        .file("src/main.rs", "fn main() {}")
+        .file("src/bin/bar.rs", "fn main() {}")
+        .file("src/bin/bar2.rs", "fn main() {}")
+        .file("src/bin/main.rs", "fn main() {}")
+        .file("src/bin/bar3/main.rs", "fn main() {}")
+        .file("src/bin/bar4/main.rs", "fn main() {}");
+
+    assert_that(p.cargo_process("build"), execs().with_status(0));
+    assert_that(&p.bin("foo"), existing_file());
+    assert_that(&p.bin("bar"), existing_file());
+    assert_that(&p.bin("bar2"), existing_file());
+    assert_that(&p.bin("bar3"), existing_file());
+    assert_that(&p.bin("bar4"), existing_file());
+    assert_that(&p.bin("main"), existing_file());
+}